wip: image cutline/clamp UV bounds during mesh creation#351
Closed
kylebarron wants to merge 1 commit into
Closed
Conversation
3 tasks
kylebarron
added a commit
that referenced
this pull request
May 27, 2026
Build an axis-aligned 2-triangle rectangle seed for a UV sub-rectangle (no delaunator, runtime-safe). UNIT_SQUARE_SEED is now rectangleSeed(0,0,1,1). Used to clamp a mesh to a UV band — e.g. the valid Web Mercator latitude band (#182 / #351) — and reused by the antimeridian vertical-cut case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kylebarron
added a commit
that referenced
this pull request
May 27, 2026
Pure helper that returns a rectangleSeed clamping a north-up geographic tile's reprojection mesh to the Web Mercator latitude band, or undefined when no clamp is needed/possible (rotated/projected tiles, fully-polar tiles). Avoids the degenerate near-pole triangles from #182 / #351. Unit-tested with synthetic corner latitudes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kylebarron
added a commit
that referenced
this pull request
May 27, 2026
Wire the reprojector seed through the render path: RasterLayer gains an initialTriangulation prop (passed to RasterReprojector, regenerated on change); getTileMetadata computes a per-tile _webMercatorReprojectorSeed via webMercatorClampSeed; _renderSubLayers passes it in the Web Mercator branch only (globe shows the poles, full mesh). Fixes the degenerate near-pole triangles for EPSG:4326 imagery reaching ±90° (#182 / #351). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kylebarron
added a commit
that referenced
this pull request
May 27, 2026
…hes to ±85.051° (#574) * docs(specs): add antimeridian crossing-tile (cut-in-two) design Design for rendering imagery crossing ±180° in Web Mercator (issues #171, #366). Splits a crossing tile at the antimeridian into west/east pieces so each reprojects as a normal non-crossing tile — avoiding the proj4-rewrap unwrap that prior attempts (#353/#374/#269) stumbled on. Generalizes the RasterReprojector to accept a delaunator-shaped initial-triangulation seed (subsuming #351 uvBounds / pole clamp), splits in _renderSubLayers into two single-mesh RasterLayers, and uses a two-box bounding volume composing with the merged world-copy traversal (#518). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(specs): refine antimeridian spec — add test fixture, handle slanted cuts - Use the vendored geotiff-test-data antimeridian.tif fixture (42x42 EPSG:4326, crosses -180 at column 24) as the primary deterministic crossing test. - Handle slanted (rotated-geotransform) cuts, not just vertical: any straight cut yields convex pieces delaunator triangulates exactly; error only on curved/concave cuts. createInitialConditions is therefore part of the MVP crossing path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(raster-reproject): add createInitialConditions (delaunator-backed seed) Add the InitialTriangulation type and a tree-shakeable createInitialConditions helper that builds a Delaunay seed from a UV point set. delaunator is confined to its own module (initial-conditions.ts); delatin.ts does not import it and the package is sideEffects:false, so it tree-shakes out for consumers that don't use it. Foundation for antimeridian cut-in-two and the sub-domain capability in #351. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(raster-reproject): document delaunator seed pattern, don't ship a wrapper Per review: a one-line delaunator wrapper isn't worth a runtime dependency. Expose only the InitialTriangulation type and show the delaunator one-liner in its docstring. delaunator moves to devDependencies — used by tests to validate winding compatibility, not shipped to consumers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(specs): drop createInitialConditions wrapper from antimeridian design raster-reproject exposes only the InitialTriangulation type + documents the delaunator one-liner; delaunator is a dev/test dep (winding validation), not shipped. Runtime seed-building for crossing tiles is the deck.gl-raster builder's job (follow-up plan). Mark stage 1 done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(raster-reproject): seed RasterReprojector from an initial triangulation Generalize the constructor to accept an optional initialTriangulation seed (delaunator's data shape), defaulting to a hardcoded unit-square seed so behavior is unchanged and the package needs no runtime triangulation dep. Refinement only ever splits existing triangles, so a sub-domain seed confines the mesh to that region. Tests build seeds via delaunator (the documented pattern) to validate winding compatibility + sub-domain confinement. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(raster-reproject): add rectangleSeed helper for sub-rectangle seeds Build an axis-aligned 2-triangle rectangle seed for a UV sub-rectangle (no delaunator, runtime-safe). UNIT_SQUARE_SEED is now rectangleSeed(0,0,1,1). Used to clamp a mesh to a UV band — e.g. the valid Web Mercator latitude band (#182 / #351) — and reused by the antimeridian vertical-cut case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(deck.gl-raster): add webMercatorClampSeed (clamp mesh to ±85.051°) Pure helper that returns a rectangleSeed clamping a north-up geographic tile's reprojection mesh to the Web Mercator latitude band, or undefined when no clamp is needed/possible (rotated/projected tiles, fully-polar tiles). Avoids the degenerate near-pole triangles from #182 / #351. Unit-tested with synthetic corner latitudes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(deck.gl-raster): clamp Web Mercator meshes to valid latitude band Wire the reprojector seed through the render path: RasterLayer gains an initialTriangulation prop (passed to RasterReprojector, regenerated on change); getTileMetadata computes a per-tile _webMercatorReprojectorSeed via webMercatorClampSeed; _renderSubLayers passes it in the Web Mercator branch only (globe shows the poles, full mesh). Fixes the degenerate near-pole triangles for EPSG:4326 imagery reaching ±90° (#182 / #351). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(deck.gl-raster): name clamp seed as initialTriangulation Rename webMercatorClampSeed → webMercatorInitialTriangulation and the tile metadata field _webMercatorReprojectorSeed → _webMercatorInitialTriangulation, for consistency with the InitialTriangulation type and the RasterLayer.initialTriangulation prop (drops the ad-hoc 'seed'/'ReprojectorSeed' terms). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(deck.gl-raster): rename clamp fn to createInitialWebMercatorTriangulation Verb-prefixed name for the builder (was webMercatorInitialTriangulation); the tile metadata field stays _webMercatorInitialTriangulation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(raster-reproject): rename rectangleSeed -> triangulateRectangle Active verb name for the helper that triangulates a UV rectangle into an InitialTriangulation; internal UNIT_SQUARE_SEED -> UNIT_SQUARE_TRIANGULATION; test file renamed to match. Updates the deck.gl-raster clamp caller too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: apply triangulateRectangle rename across source + caller Completes the rename (a657eb8 only moved the test file): rectangleSeed -> triangulateRectangle in delatin.ts + index export + the renamed test, and the deck.gl-raster web-mercator-clamp caller. UNIT_SQUARE_SEED -> UNIT_SQUARE_TRIANGULATION. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Right now, our RasterReprojector always renders the entire input image. That is, the two initial triangles drawn span the entire bounds of the input image.
But sometimes this is not actually desired.
rendering images near the poles in Web Mercator
For example, this branch started during exploration of #182. When rendering data from EPSG:4326 which, say it spans from (-180, -90, 180, 90). Well, -90 and +90 are outside of Web Mercator latitude bounds, which are ~ +-85.11.
When we try to render such an image, we get very tall triangles near the poles, because essentially we never have enough triangles to accurately approximate the reprojection near the poles. The only reason why this renders at all is because we stop the delatin refinement after 10,000 iterations.
Instead of using the full height of the image, we should probably just discard the image outside of 85.11, and not even try to render data there.
Excluding parts of images that aren't intended to be renderedEdit: This was implemented in a different way in #424
I've considered this before but haven't written it down... I'd like to render usgs-topo-tiler directly from the client. But those images have a "collar" that overlaps other adjacent images and isn't intended to be rendered.
We can use this "cutline" to cut that out and not render it.
What should the API be?
I want to think a bit more about this API before merging and making it public.
I think perhaps the RasterReprojector should support
uvBoundswhich are allowed to be between 0 and 1. Not less than 0 and not greater than 1.This assumes always a square cutline, but I think that's ok for now.